import 'dart:math';

import 'package:flutter/material.dart';

class DotFont extends StatefulWidget {
  DotFont({Key? key}) : super(key: key);

  @override
  State<DotFont> createState() => _DotFontState();
}

class _DotFontState extends State<DotFont> with SingleTickerProviderStateMixin {
  late var startPositions = <Offset>[];
  late Animation<double> animation;
  late AnimationController controller;
  final dao = [
    0x0100,
    0x0200,
    0x1FF0,
    0x1010,
    0x1210,
    0x1150,
    0x1020,
    0x1000,
    0x1FFC,
    0x0204,
    0x2224,
    0x2224,
    0x3FE4,
    0x0004,
    0x0028,
    0x0010
  ];

  @override
  void initState() {
    super.initState();
    var wordBitCount = 0;
    for (var hex in dao) {
      wordBitCount += _countBitOne(hex);
    }
    startPositions = List.generate(wordBitCount, (index) {
      return Offset(
        Random().nextDouble(),
        Random().nextDouble(),
      );
    });

    controller =
        AnimationController(duration: const Duration(seconds: 1), vsync: this);
    animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
      parent: controller,
      curve: Curves.easeOut,
    ))
      ..addListener(() {
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomPaint(
        painter: FontPainter(
          startPositions: startPositions,
          wordHex: dao,
          animationValue: animation.value,
        ),
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
        ),
      ),
      bottomSheet: FloatingActionButton(
        onPressed: () {
          if (controller.status == AnimationStatus.completed) {
            controller.reverse();
          } else {
            controller.forward();
          }
        },
        child: Icon(
          Icons.play_arrow,
          color: Colors.blue,
        ),
      ),
    );
  }

  int _countBitOne(int hex, {int bitLength = 16}) {
    int result = 0;
    for (var bit = 0; bit < bitLength; bit++) {
      if (hex & (2 << bit) != 0) {
        result++;
      }
    }

    return result;
  }
}

class FontPainter extends CustomPainter {
  final List<Offset> startPositions;
  final List<int> wordHex;
  final double animationValue;
  FontPainter(
      {required this.startPositions,
      required this.wordHex,
      required this.animationValue});

  @override
  void paint(Canvas canvas, Size size) {
    final dotCount = 16;
    final fontSize = 100.0;
    var radius = fontSize / dotCount;
    var startPos =
        Offset(size.width / 2 - fontSize, size.height / 2 - fontSize);
    var paint = Paint()..color = Colors.blue[600]!;

    var paintIndex = 0;
    for (int i = 0; i < dotCount; ++i) {
      var position = startPos + Offset(0.0, radius * i * 2);
      for (int j = 0; j < dotCount; ++j) {
        // 判断第 i 行第几位不为0，不为0则绘制，否则不绘制
        if ((wordHex[i] & ((1 << dotCount - j))) != 0) {
          var startX = startPositions[paintIndex].dx * size.width;
          var startY = startPositions[paintIndex].dy * size.height;
          var endX = startPos.dx + radius * j * 2;
          var endY = position.dy;
          var animationPos = Offset(startX + (endX - startX) * animationValue,
              startY + (endY - startY) * animationValue);
          canvas.drawCircle(animationPos, radius, paint);
          paintIndex++;
        }
      }
    }

    // 点阵字效果绘制
    // for (int i = 0; i < dotCount; ++i) {
    //   var position = startPos + Offset(0.0, radius * i * 2);
    //   for (int j = 0; j < dotCount; ++j) {
    //     var dotPosition = startPos + Offset(radius * 2 * j, position.dy);

    //     if ((wordHex[i] & ((1 << dotCount - j - 1))) != 0) {
    //       paint.color = Colors.blue[600]!;
    //       canvas.drawCircle(dotPosition, radius, paint);
    //     } else {
    //       paint.color = Colors.grey;
    //       canvas.drawCircle(dotPosition, radius, paint);
    //     }
    //   }
    // }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}
